/* + C O P Y R I G H T * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* See top source directory for COPYRIGHT.txt                              */
/* Copyright 2002-2003 by Mark Wolgemuth, All rights reserved.             */
/* Copyright 2002-2003 by Employease, Inc.  All rights reserved.           */
/* - C O P Y R I G H T * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "athd.h"

apr_status_t
athd_cmd_io_accept(athd_cmd_handler *handler, apr_socket_t *socket)
{
    apr_status_t rv;
    ASSERT(handler);
    ASSERT(socket);
    rv = apr_socket_accept(&handler->socket, socket, handler->pool);
    if ((!rv) && (handler->socket))
      {
	  //apr_socket_opt_set(handler->socket, APR_SO_DEBUG, 1);
	  apr_socket_opt_set(handler->socket, APR_SO_LINGER, 1);
	  //apr_socket_opt_set(handler->socket, APR_SO_RCVBUF, 512);
	  //apr_socket_opt_set(handler->socket, APR_SO_SNDBUF, 512);
	  //apr_socket_opt_set(handler->socket, APR_INCOMPLETE_READ, 1);
	  apr_socket_timeout_set(handler->socket, 
				 apr_time_from_sec(ATHD_CMD_SESSION_TIMEOUT));
	  
	  pthread_create(handler->thread, NULL, handler->thread_func, handler);
      }
    return rv;
}
     
apr_status_t
athd_cmd_io_recv_str(athd_cmd_req *req, char **str)
{
    char *strrecv = NULL;
    int bufsize = 512;
    char *buf;
    int rv = APR_SUCCESS;
    apr_size_t len;
    apr_size_t lentotal = 0;
    ASSERT(req);

    buf = apr_pcalloc(req->pool, sizeof(char) * (bufsize + 1));
    for (; ((!rv) && (!APR_BRIGADE_EMPTY(req->bb_recv))); )
      {
	  apr_size_t read;
	  apr_size_t readsofar = 0;
	  apr_bucket *bk;
	  const char *data;
	  ASSERT(req->ba_recv);

	  bk = APR_BRIGADE_FIRST(req->bb_recv);
	  APR_BUCKET_REMOVE(bk);
	  if (APR_BUCKET_IS_EOS(bk))
	      break;
	  rv = apr_bucket_read(bk, &data, &len, APR_BLOCK_READ);

	  lentotal += len;
	  if (lentotal > ATHD_MAX_CMD_RECV)
	    {
		rv = APR_EGENERAL;
		athd_log(ATHD_WARN, ATHD_ERR_IORCVLIM);
	    }
	  while ((!rv) && (readsofar < len))
	    {
		if (len > bufsize)  
		    read = bufsize;
		else  
		    read = len;
		memcpy(buf, data, read);
		*(buf + read) = (int)NULL;
		readsofar += read;
		req->recved += read;
		if (strrecv)
		    strrecv = apr_pstrcat(req->pool, strrecv, buf, NULL);
		else
		    strrecv = apr_pstrcat(req->pool, buf, NULL);
	    }
	  if ((len > 1) && ((buf[len - 2] == '\r') && (buf[len - 2] == '\n')))
	      break;
      }
    if (APR_STATUS_IS_ECONNRESET(rv))
	athd_log_err(ATHD_DEBUG, rv, "Cmd io RECV: ");
    else if (rv)
	athd_log_err(ATHD_WARN, rv, "Cmd io RECV: ");
    (*str) = strrecv;
    athd_log(ATHD_DEVEL, "Cmd io RECV: [%s]", strrecv ? strrecv : "(null)");
    apr_brigade_cleanup(req->bb_recv);
    return rv;
}

apr_status_t 
athd_cmd_io_send_add(athd_cmd_req *req, const char *buf)
{
    apr_size_t len;
    ASSERT(req);
    ASSERT(buf);
    len = strlen(buf);
    req->tosend += len;
    athd_log(ATHD_DEVEL, "Cmd io handler: [%d] SEND queue: [%s]",
	     req->handler->id, buf);
    return apr_brigade_puts(req->bb_send, NULL, NULL, buf);
}

apr_status_t
athd_cmd_io_send_flush(athd_cmd_req *req)
{
    apr_status_t rv = APR_SUCCESS;
    apr_bucket *bk;
    apr_size_t done = 0;
    apr_size_t sent = 0;
    struct iovec vec[ATHD_CMD_SEND_IOVEC];
    int veci;
    int i = 0;
    int ipipe = 0;

    ASSERT(req);

    if (req->tosend > ATHD_MAX_CMD_SEND)
      {
	  athd_log(ATHD_WARN, ATHD_ERR_IOSNDLIM);
	  rv = APR_EGENERAL;
      }
    else if ((req->handler->type == ATHD_CMD_STREAM) && (req->tosend))
      {
	  apr_bucket_alloc_t *ba = apr_bucket_alloc_create(req->pool);
	  apr_bucket_brigade *bb = apr_brigade_create(req->pool, ba);
	  char *sthead = 
	    apr_psprintf(req->pool, "Content-Length: %d\n", req->tosend);
	  apr_size_t len = strlen(sthead);
	  req->tosend += len;
	  apr_brigade_puts(bb, NULL, NULL, sthead);
	  APR_BRIGADE_PREPEND(req->bb_send, bb);
	  RV_OK = athd_cmd_io_send_add(req, "\r\n");
      }
    /* create prompt */
    else if (req->handler->type == ATHD_CMD_SHELL)
      {
	  athd_cmd_parser_prompter(req);
	  RV_OK = athd_cmd_io_send_add(req, req->handler->prompt ?
				       req->handler->prompt : 
				       ATHD_CMD_PROMPT_BASE);
      }
	
    bk = apr_bucket_eos_create(req->ba_send);
    APR_BRIGADE_INSERT_TAIL(req->bb_send, bk);
    RV_OK = apr_brigade_to_iovec(req->bb_send, &vec[0], &veci);
    while ((APR_STATUS_IS_SUCCESS(rv)) && (ipipe < ATHD_BROKEN_PIPE_DIE)
	   && (done != req->tosend))
      {
	  sent = req->tosend - done;
	  RV_OK = apr_socket_sendv(req->handler->socket, 
				   vec + i, veci - i, &sent);
	  if (!sent)
	      ipipe++;
	  athd_log(ATHD_DEVEL,"Cmd io handler: [%d] SEND bytes: [%d/%d]",
		   req->handler->id, sent, req->tosend);
	  done += sent;
	  if (done < req->tosend)
	    {
		apr_size_t cnt = vec[i].iov_len;
		while ((sent >= cnt) && (i + 1 < veci))
		  {
		      i++;
		      cnt += vec[i].iov_len;
		  }
		if (sent < cnt)
		  {
		      vec[i].iov_base = (char *) vec[i].iov_base +
			(vec[i].iov_len - (cnt - sent));
		      vec[i].iov_len = cnt - sent;
		  }
	    }
      }
    apr_brigade_cleanup(req->bb_send);
    req->tosend = 0;
    return rv;
}
